#include "stdafx.h"
#include "sensor.h"
#include "title.h"

using namespace std;


SensorEditor::SensorEditor(Common* _common, SceneView* _scene) : common(_common), scene(_scene)
	//: QDialog(pwgt, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
{
	ui.setupUi(this);
	connect(ui.bAdd, SIGNAL(clicked), this, SLOT(on_bAdd_clicked));
	connect(ui.bRemove, SIGNAL(clicked), this, SLOT(on_bRemove_clicked));
	connect(ui.listOfSensor, SIGNAL(currentRowChanged(int)), this, SLOT(displayParameters(int)));
	connect(ui.listOfSensor, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(itemClicked(QListWidgetItem*)));
	connect(ui.listOfParameter, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(parameterStateChanged(QListWidgetItem*)));
	connect(ui.listOfParameter, SIGNAL(currentRowChanged(int)), this, SLOT(displayModel(int)));
	parameters = g_units.getCalcParameters();
}

void SensorEditor::on_bAdd_clicked(){
	automaticItem* aut_item = scene->addAutomaticItem(QPointF(0,0), automaticItem::SENSOR, pipe->getTitle()->getText());
	connect(aut_item->getSensor(), SIGNAL(updatePosition(Sensor*)), pipe, SLOT(updateSensorPosition(Sensor*)));
	connect(aut_item, SIGNAL(updateName(const QString &)), this, SLOT(updateSensorName(const QString &)));
	list_of_item.push_back(aut_item);
	ui.listOfSensor->addItem(aut_item->getName());
	ui.listOfSensor->setCurrentRow(ui.listOfSensor->count() - 1);
	displayParameters(ui.listOfSensor->currentRow());	
}

void SensorEditor::on_bRemove_clicked(){
	if (ui.listOfSensor->currentRow() == -1){
		QMessageBox::information(0, tr("Removing sensor..."), tr("Sensor is not selected!"), QMessageBox::Ok);	
		return;
	}
	if(QMessageBox::question(0, tr("Removing sensor..."), QString(tr("Remove the sensor: '%1'?")).arg(ui.listOfSensor->currentItem()->text()),
		QMessageBox::Yes |QMessageBox::No) == QMessageBox::Yes)
	{	
		int sensor_index = ui.listOfSensor->currentRow();
		delete ui.listOfSensor->takeItem(sensor_index);
		delete list_of_item[sensor_index];
		list_of_item.erase(list_of_item.begin() + sensor_index);
		
		displayParameters(ui.listOfSensor->currentRow());	
	}
}

void SensorEditor::displayParameters(int index){
	if (index == -1){
		ui.listOfParameter->clear();
		return;
	}
	ui.listOfParameter->clear();
	vector<pair<QString, bool>> list = list_of_item[index]->getSensor()->getListOfParameter();
	for (int i = 0; i < list.size(); i++){
		QListWidgetItem *item = new QListWidgetItem(list[i].first);
		if (list[i].second) item->setCheckState(Qt::Checked);
		else item->setCheckState(Qt::Unchecked);
		item->setTextColor(list[i].second ? Qt::black : Qt::gray);
		ui.listOfParameter->addItem(item);
	}
	setModel(list_of_item[ui.listOfSensor->currentRow()]->getModel());
	ui.trParameters->setEnabled(true);
	common->updateScript(ui.trParameters);
}

void SensorEditor::itemClicked(QListWidgetItem *item){
	displayParameters(ui.listOfSensor->currentRow());
}

void SensorEditor::parameterStateChanged(QListWidgetItem *item){
	QString name = item->text();
	bool state = item->checkState() == Qt::Checked;
	list_of_item[ui.listOfSensor->currentRow()]->getSensor()->setParameterState(name, state);
	ui.trParameters->setEnabled(state);

	ui.listOfParameter->setCurrentItem(item);
	displayModel(ui.listOfSensor->currentRow());
	item->setTextColor(state ? Qt::black : Qt::gray);
}

void SensorEditor::displayModel(int index){
	if (index == -1){
		setModel(new ParametersModel());
		return;
	}
	QString parameter_name = ui.listOfParameter->currentItem()->text();
	if (!parameters.filter(parameter_name).count())
		return;
	setModel(list_of_item[ui.listOfSensor->currentRow()]->getSensor()->getParameterModel(parameter_name));

	bool state = list_of_item[ui.listOfSensor->currentRow()]->getSensor()->getState(parameter_name);
	ui.trParameters->setEnabled(state);
	common->updateScript(ui.trParameters);

}

void SensorEditor::setModel(ParametersModel* _model){
	ui.trParameters->setModel(_model);
	ui.trParameters->setItemDelegate(new Delegate(ui.trParameters));
	ui.trParameters->setStyleSheet("alternate-background-color: rgb(190, 234, 235)");
	for(int i = 3; i <= 8; i++)
		ui.trParameters->hideColumn(i);
	ui.trParameters->header()->setStretchLastSection(false);
	ui.trParameters->header()->setSectionResizeMode(1, QHeaderView::Stretch);   
	ui.trParameters->setColumnWidth(0, 120);
	ui.trParameters->setColumnWidth(2, 40);
}

void SensorEditor::setData(vector<automaticItem*> _list_of_item, hydraulicItem* _pipe){
	pipe = _pipe;
	list_of_item = _list_of_item;
	foreach(automaticItem* aut_data, list_of_item){
		ui.listOfSensor->addItem(aut_data->getName());
		connect(aut_data, SIGNAL(updateName(const QString &)), this, SLOT(updateSensorName(const QString &)));
	}
	if (ui.listOfSensor->count()) ui.listOfSensor->setCurrentRow(0);
}

void SensorEditor::setSensor(automaticItem *_sensor){
	list_of_item.push_back(_sensor);
	ui.listOfSensor->addItem(_sensor->getSensor()->getName());
	connect(_sensor, SIGNAL(updateName(const QString &)), this, SLOT(updateSensorName(const QString &)));
	ui.listOfSensor->setCurrentRow(0);
	ui.bAdd->setVisible(false);
	ui.bRemove->setVisible(false);
}

vector<automaticItem*> SensorEditor::getData(){
	return list_of_item;
}

void SensorEditor::updateSensorName(const QString &name){
	ui.listOfSensor->currentItem()->setText(name);
}

//=================================================================================================================
Sensor::Sensor(Common* _common, SceneView* _scene, automaticItem* _parent, const bool display_title)
	: common(_common), scene(_scene), parent(_parent)
{
	init_model();
	QStringList _parameters = g_units.getCalcParameters();
	foreach(QString p, _parameters){
		list_of_parameters[p.toStdString()] = make_pair(init_parameter_model(p), false);
	}
	vector<QString> list_of_script = scene->common->getScriptList();
	foreach(QString script_name, list_of_script)
		addScriptParameter(script_name);
	connect(common, SIGNAL(layer_is_added(const double &)), this, SLOT(addLayer(const double &)));

}

Sensor::~Sensor(){
	//common->sensorDeleted(this);
	//delete indicator;
	//delete model;
	//delete title;
}


void Sensor::init_model(){
	//0 - combobox/boolean	//1 - spinbox
	//2 - textbox	//3 - label  //4 combo
	QString common_parameter = "COMMON";
	ParametersModel *model = parent->getModel();;
	model->addItem(common_parameter, "COORDINATE", 0, "m", 1, 1,"coordinate");
	model->addItem(common_parameter, "DISPLAYED", true, "-", 0, 2,"show");

	connect(this, SIGNAL(setNewParameterValue(const QString &, const QVariant &)), 
		model, SLOT(changeData(const QString &, const QVariant &)));

	connect(model, SIGNAL(ItemChanged(const QString &, const QVariant &)),
		this, SLOT(parent->getNewParameterValue(const QString &, const QVariant &)));

	connect(common, SIGNAL(scriptAdded(const QString&)), this, SLOT(addScriptParameter(const QString&)));
	connect(common, SIGNAL(scriptRemoved(const QString&)), this, SLOT(removeScriptParameter(const QString&)));
}

ParametersModel* Sensor::init_parameter_model(const QString &_parameter){
	ParametersModel* model = new ParametersModel(this);
	QString unit = common->getDefaultUnitText(_parameter);
	QStringList unit_list = g_units.getUnitList(unit);
	QString unit_data;
	int default_unit_index;
	for(int i = 0; i < unit_list.count(); ++i){
		if (!unit_data.isEmpty()) unit_data += ";";
		if (unit_list[i] == unit) default_unit_index = i;
		unit_data += unit_list[i];
	}
	unit_data = QString::number(default_unit_index) + "//" + unit_data;
	QString common_parameter = "COMMON";
	model->addItem(common_parameter, "DIGITS", 2, "", 1, 1,"number of digits after the decimal point");
	model->addItem(common_parameter, "UNIT", unit_data, "", 4, 1,"default unit");
	return model;
}


void Sensor::addScriptParameter(const QString &script_name){	
	QString unit_time = common->getDefaultUnitText("TIME");
	for (hash_map<string, pair<ParametersModel*, bool> >::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		ParametersModel *_model = it->second.first;
		QString unit = common->getDefaultUnitText(QString::fromStdString(it->first));
		_model->addItem(script_name, "A", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit), "edit", 5, 0);
		_model->addItem(script_name, "T", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit_time), "edit", 5, 0);
		_model->addItem(script_name, "B", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit), "edit", 5, 0);
		_model->addItem(script_name, "C", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit), "edit", 5, 0);
	}

}

void Sensor::removeScriptParameter(const QString &script_name){
	for (hash_map<string, pair<ParametersModel*, bool> >::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		it->second.first->removeItems(script_name);
}

/*
void Sensor::update_parameters(){
	common->getParametersWidget()->setItemDelegate(new Delegate());
}*/

ParametersModel* Sensor::getParameterModel(QString _name){
	return list_of_parameters[_name.toStdString()].first;
}

bool Sensor::getState(QString _name){
	return list_of_parameters[_name.toStdString()].second;
}


vector<pair<QString, bool>> Sensor::getListOfParameter() {
	vector<pair<QString, bool>> list;

	for (hash_map<string, pair<ParametersModel*, bool> >::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		QString name = QString::fromStdString(it->first);
		bool checked = it->second.second;
		list.push_back(make_pair(name, checked));
	}
	return list;
}

void Sensor::setParameterState(const QString &_name, const bool &_state){
	for (hash_map<string, pair<ParametersModel*, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if(QString::fromStdString(it->first) == _name){
			it->second.second = _state;
			break;
		}
}

/*
void Sensor::getNewParameterValue(const QString &_name, const QVariant &_value){
	if (_name == "NAME"){
		name = _value.toString();
		emit updateName(name);
	}
}*/


/*
void Sensor::adjust(const QPointF &_pos){
	if(graphical_item) graphical_item->setPos(QPointF(_pos.x(), _pos.y() - (graphical_item->getSize().height()/2 + 9)));
	if(indicator) indicator->adjust(graphical_item->pos());
	if(title) title->adjust(graphical_item->pos());
}*/


/*

Title* Sensor::getTitle(){
	return title;
}

void Sensor::setTitle(Title *_title){
	title = _title;
}
*/

void Sensor::showSensorEditor(){
	SensorEditor sensor_editor(common, scene);
	sensor_editor.setSensor(parent);
	if (sensor_editor.exec()){
		/*Sensor* temp = sensor_editor.getData()[0];
		this = temp;*/
		if (parent->isVisibleOnScene())
			scene->addItem(parent);
		else
			scene->removeItem(parent);
		QStringList calculatedParameters = getCalculatedParameters();
		QStringList units_for_calculatedParameters;
		QVector<int> digits;
		foreach(QString parameter, calculatedParameters){
			units_for_calculatedParameters.push_back(getUnit(parameter.toStdString()));
			digits.push_back(getDigits(parameter.toStdString()));
		}
		parent->getIndicator()->updateParameters(calculatedParameters, units_for_calculatedParameters, digits);
		//emit parent->updatePosition(this);////////////////////////////////////////////
	}
}

double Sensor::getParameterError(const string &_parameter, const QString &_script, const double &_time){
	double t = list_of_parameters[_parameter].first->getTimeVaryingParameter("T", _time, _script);
	double _sin = abs(t) < 1e-2 ? 0 : sin(2 * 3.14 * _time / t );
	double a = list_of_parameters[_parameter].first->getTimeVaryingParameter("A", _time, _script);
	double b = list_of_parameters[_parameter].first->getTimeVaryingParameter("B", _time, _script);
	double c = list_of_parameters[_parameter].first->getTimeVaryingParameter("C", _time, _script);
	double rnd = rand() / 32768.0 * 2 - 1;
	
	double error = a * _sin + b + c * rnd; 
	return error;
}

QStringList Sensor::getCalculatedParameters(){
	QStringList list;
	for (hash_map<string, pair<ParametersModel*, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if (it->second.second)
			list.push_back(QString::fromStdString(it->first));
	return list;
}


int Sensor::countCalcParameters(){
	int count = 0;
	for (hash_map<string, pair<ParametersModel*, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if(it->second.second)
			count++;
	return count;
}

ParametersModel* Sensor::addParameter(const QString &_name, const bool &_calculated){ 
	bool parameter_exist = false;
	for (hash_map<string, pair<ParametersModel*, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		if(it->first == _name.toStdString()){
			parameter_exist = true;
			break;
		}
	}
	ParametersModel* model;
	if (parameter_exist){
		model = list_of_parameters[_name.toStdString()].first;
	}else{
		model = init_parameter_model(_name);
		vector<QString> script_list = common->getScriptList();
		QString unit_time = common->getDefaultUnitText("TIME");
		foreach(QString script_name, script_list){
			QString unit = common->getDefaultUnitText(_name);
			model->addItem(script_name, "A", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit), "edit", 5, 1);
			model->addItem(script_name, "T", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit_time), "edit", 5, 1);
			model->addItem(script_name, "B", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit), "edit", 5, 1);
			model->addItem(script_name, "C", QString("both:piecewise-constant//TIME:ERROR//s:%1//").arg(unit), "edit", 5, 1);
		}
	}
	list_of_parameters[_name.toStdString()] = make_pair(model, _calculated);
	return model;
}


bool Sensor::isCalculatedParameters( const string &_name )
{
	return list_of_parameters[_name].second;
}

Indicator* Sensor::getIndicator(){
	return parent->getIndicator();
}

int Sensor::getDigits( const string &_name ){
	return list_of_parameters[_name].first->getNumParameter("DIGITS");
}

QString Sensor::getUnit( const string &_name ){
	return list_of_parameters[_name].first->getStringParameter("UNIT");
}

double Sensor::getCoordinate(){
	return parent->getNumParameter("COORDINATE");
}

const hash_map<string, pair<ParametersModel*, bool> > & Sensor::getListOfParameters(){
	return list_of_parameters;
}

/*
ParametersModel* Sensor::getModel(){
	return model;
}*/

QString Sensor::getName(){
	return parent->getName();
}

QStringList Sensor::getEnabledParametersName(){
	QStringList list;
	for (hash_map<string, pair<ParametersModel*, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if (it->second.second)
			list.push_back(QString::fromStdString(it->first));
	return list;
}

double Sensor::getWidth(){
	return parent->getSize().width();
}

void Sensor::popupShow(QGraphicsSceneContextMenuEvent *event){
	QMenu menu;	
	bool enabledAct = countCalcParameters();
	QAction* actIndicator = new QAction("indicator", this);
	connect(actIndicator, SIGNAL(triggered()), parent, SLOT(showIndicator()));
	actIndicator->setCheckable(true);
	actIndicator->setChecked(parent->getIndicator()->isVisible());
	actIndicator->setEnabled(enabledAct);
	QAction* actIndicatorLine = new QAction(/*auto-hide*/"line", this);
	connect(actIndicatorLine, SIGNAL(triggered()), parent, SLOT(showIndicatorLine()));
	actIndicatorLine->setCheckable(true);
	actIndicatorLine->setChecked(parent->getIndicator()->lineIsVisible());
	actIndicatorLine->setEnabled(parent->getIndicator()->isVisible());
	actIndicatorLine->setEnabled(enabledAct);
	QAction* actEdit = new QAction("edit...", this);
	connect(actEdit, SIGNAL(triggered()), this, SLOT(showSensorEditor()));
	menu.addAction(actIndicator);
	menu.addAction(actIndicatorLine);
	menu.addAction(actEdit);
	menu.exec(event->screenPos());
}

void Sensor::addLayer( const double &time ){
	hydraulicItem *pipe = scene->findHydItemByName(parent->getParentName());
	double sensor_coordinate = getCoordinate();
	QVector<double> coordinate = pipe->getCoordinate();
	int first_index = 0;
	int last_index = coordinate.size() - 1;
	while (last_index - first_index > 1){
		int middle_index = (last_index + first_index) / 2;
		if (sensor_coordinate > coordinate[middle_index]){
			first_index = middle_index;
		}else{
			last_index = middle_index;
		}
	}
	double coeff = (sensor_coordinate - coordinate[first_index]) / (coordinate[last_index] - coordinate[first_index]);
	QStringList keywordList = getCalculatedParameters();
	foreach(QString keyword, keywordList){
		string s_keyword = keyword.toStdString();
		QVector<double> values = common->get_back_values_from_hyd_item(pipe, wstring(s_keyword.begin(), s_keyword.end())); 
		double value = values[first_index] + (values[last_index] - values[first_index]) * coeff;
		double error = getParameterError(s_keyword, common->getCurrentScript(), time);
		parent->addDataLayer(keyword, value + error);
	}
}
